/**
 * ***************************************************************************** Copyright (c) 2007,
 * 2010 IBM Corporation and others. All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0 which accompanies this
 * distribution, and is available at http://www.eclipse.org/legal/epl-v10.html
 *
 * <p>Contributors: IBM Corporation - initial API and implementation
 * *****************************************************************************
 */
package org.eclipse.ltk.internal.core.refactoring.resource;

import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.mapping.IResourceChangeDescriptionFactory;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.ltk.core.refactoring.Change;
import org.eclipse.ltk.core.refactoring.RefactoringChangeDescriptor;
import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
import org.eclipse.ltk.core.refactoring.RefactoringStatus;
import org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext;
import org.eclipse.ltk.core.refactoring.participants.ParticipantManager;
import org.eclipse.ltk.core.refactoring.participants.RefactoringParticipant;
import org.eclipse.ltk.core.refactoring.participants.RenameArguments;
import org.eclipse.ltk.core.refactoring.participants.RenameProcessor;
import org.eclipse.ltk.core.refactoring.participants.ResourceChangeChecker;
import org.eclipse.ltk.core.refactoring.participants.SharableParticipants;
import org.eclipse.ltk.core.refactoring.resource.RenameResourceChange;
import org.eclipse.ltk.core.refactoring.resource.RenameResourceDescriptor;
import org.eclipse.ltk.internal.core.refactoring.BasicElementLabels;
import org.eclipse.ltk.internal.core.refactoring.Messages;
import org.eclipse.ltk.internal.core.refactoring.RefactoringCoreMessages;
import org.eclipse.ltk.internal.core.refactoring.Resources;

/**
 * A rename processor for {@link IResource}. The processor will rename the resource and load rename
 * participants if references should be renamed as well.
 *
 * @since 3.4
 */
public class RenameResourceProcessor extends RenameProcessor {

  private IResource fResource;
  private String fNewResourceName;
  private boolean fUpdateReferences;
  private RenameArguments fRenameArguments; // set after checkFinalConditions

  /**
   * Creates a new rename resource processor.
   *
   * @param resource the resource to rename.
   */
  public RenameResourceProcessor(IResource resource) {
    if (resource == null || !resource.exists()) {
      throw new IllegalArgumentException("resource must not be null and must exist"); // $NON-NLS-1$
    }

    fResource = resource;
    fRenameArguments = null;
    fUpdateReferences = true;
    setNewResourceName(resource.getName()); // Initialize new name
  }

  /**
   * Returns the resource this processor was created on
   *
   * @return the resource to rename
   */
  public IResource getResource() {
    return fResource;
  }

  /**
   * Returns the new resource name
   *
   * @return the new resource name
   */
  public String getNewResourceName() {
    return fNewResourceName;
  }

  /**
   * Sets the new resource name
   *
   * @param newName the new resource name
   */
  public void setNewResourceName(String newName) {
    Assert.isNotNull(newName);
    fNewResourceName = newName;
  }

  /**
   * Returns <code>true</code> if the refactoring processor also updates references
   *
   * @return <code>true</code> if the refactoring processor also updates references
   */
  public boolean isUpdateReferences() {
    return fUpdateReferences;
  }

  /**
   * Specifies if the refactoring processor also updates references. The default behaviour is to
   * update references.
   *
   * @param updateReferences <code>true</code> if the refactoring processor should also updates
   *     references
   */
  public void setUpdateReferences(boolean updateReferences) {
    fUpdateReferences = updateReferences;
  }

  /* (non-Javadoc)
   * @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor#checkInitialConditions(org.eclipse.core.runtime.IProgressMonitor)
   */
  public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException {
    return RefactoringStatus.create(Resources.checkInSync(fResource));
  }

  /* (non-Javadoc)
   * @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor#checkFinalConditions(org.eclipse.core.runtime.IProgressMonitor, org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext)
   */
  public RefactoringStatus checkFinalConditions(IProgressMonitor pm, CheckConditionsContext context)
      throws CoreException {
    pm.beginTask("", 1); // $NON-NLS-1$
    try {
      fRenameArguments = new RenameArguments(getNewResourceName(), isUpdateReferences());

      ResourceChangeChecker checker =
          (ResourceChangeChecker) context.getChecker(ResourceChangeChecker.class);
      IResourceChangeDescriptionFactory deltaFactory = checker.getDeltaFactory();

      ResourceModifications.buildMoveDelta(deltaFactory, fResource, fRenameArguments);

      return new RefactoringStatus();
    } finally {
      pm.done();
    }
  }

  /**
   * Validates if the a name is valid. This method does not change the name settings on the
   * refactoring. It is intended to be used in a wizard to validate user input.
   *
   * @param newName the name to validate
   * @return returns the resulting status of the validation
   */
  public RefactoringStatus validateNewElementName(String newName) {
    Assert.isNotNull(newName, "new name"); // $NON-NLS-1$
    IContainer c = fResource.getParent();
    if (c == null)
      return RefactoringStatus.createFatalErrorStatus(
          RefactoringCoreMessages.RenameResourceProcessor_error_no_parent);

    if (!c.getFullPath().isValidSegment(newName))
      return RefactoringStatus.createFatalErrorStatus(
          RefactoringCoreMessages.RenameResourceProcessor_error_invalid_name);

    if (c.findMember(newName) != null)
      return RefactoringStatus.createFatalErrorStatus(
          RefactoringCoreMessages.RenameResourceProcessor_error_resource_already_exists);

    RefactoringStatus result =
        RefactoringStatus.create(c.getWorkspace().validateName(newName, fResource.getType()));
    if (!result.hasFatalError())
      result.merge(
          RefactoringStatus.create(
              c.getWorkspace().validatePath(createNewPath(newName), fResource.getType())));
    return result;
  }

  protected RenameResourceDescriptor createDescriptor() {
    IResource resource = getResource();

    RenameResourceDescriptor descriptor = new RenameResourceDescriptor();
    descriptor.setProject(resource instanceof IProject ? null : resource.getProject().getName());
    descriptor.setDescription(
        Messages.format(
            RefactoringCoreMessages.RenameResourceProcessor_description,
            BasicElementLabels.getResourceName(resource)));
    descriptor.setComment(
        Messages.format(
            RefactoringCoreMessages.RenameResourceProcessor_comment,
            new String[] {
              BasicElementLabels.getPathLabel(resource.getFullPath(), false),
              BasicElementLabels.getResourceName(getNewResourceName())
            }));
    descriptor.setFlags(
        RefactoringDescriptor.STRUCTURAL_CHANGE
            | RefactoringDescriptor.MULTI_CHANGE
            | RefactoringDescriptor.BREAKING_CHANGE);
    descriptor.setResourcePath(resource.getFullPath());
    descriptor.setNewName(getNewResourceName());
    descriptor.setUpdateReferences(isUpdateReferences());
    return descriptor;
  }

  /* (non-Javadoc)
   * @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor#createChange(org.eclipse.core.runtime.IProgressMonitor)
   */
  public Change createChange(IProgressMonitor pm) throws CoreException {
    pm.beginTask("", 1); // $NON-NLS-1$
    try {
      RenameResourceChange change =
          new RenameResourceChange(fResource.getFullPath(), getNewResourceName());
      change.setDescriptor(new RefactoringChangeDescriptor(createDescriptor()));
      return change;
    } finally {
      pm.done();
    }
  }

  private String createNewPath(String newName) {
    return fResource.getFullPath().removeLastSegments(1).append(newName).toString();
  }

  /* (non-Javadoc)
   * @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor#getElements()
   */
  public Object[] getElements() {
    return new Object[] {fResource};
  }

  /* (non-Javadoc)
   * @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor#getIdentifier()
   */
  public String getIdentifier() {
    return "org.eclipse.ltk.core.refactoring.renameResourceProcessor"; // $NON-NLS-1$
  }

  /* (non-Javadoc)
   * @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor#getProcessorName()
   */
  public String getProcessorName() {
    return RefactoringCoreMessages.RenameResourceProcessor_processor_name;
  }

  /* (non-Javadoc)
   * @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor#isApplicable()
   */
  public boolean isApplicable() {
    if (fResource == null) return false;
    if (!fResource.exists()) return false;
    if (!fResource.isAccessible()) return false;
    return true;
  }

  /* (non-Javadoc)
   * @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor#loadParticipants(org.eclipse.ltk.core.refactoring.RefactoringStatus, org.eclipse.ltk.core.refactoring.participants.SharableParticipants)
   */
  public RefactoringParticipant[] loadParticipants(
      RefactoringStatus status, SharableParticipants shared) throws CoreException {
    String[] affectedNatures = ResourceProcessors.computeAffectedNatures(fResource);
    return ParticipantManager.loadRenameParticipants(
        status, this, fResource, fRenameArguments, null, affectedNatures, shared);
  }
}
